package com.easylinkin.linkappapi.listener;

/**
 * <p></p>
 *
 * @author TongJie
 * @since 2020/9/16 11:10
 */

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.easylinkin.linkappapi.alarm.entity.Alarm;
import com.easylinkin.linkappapi.alarm.mapper.AlarmMapper;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.security.mapper.LinkappUserMapper;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.codec.SerializationCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * @author tongjie
 *
 * 告警工单思路：
 * <p>1.登录后开启会话  传参数 userId 调用onOpen </p>
 * <p>2.将 当前实体放入 webSocketSet 集合中</p>
 * <p>3.当每次产生告警 检查该告警 可被List<AlarmListener> 对应的哪些userId 可见<p/>
 * <p>4.向相应的userId 的session 发送消息 5.登出或会话超时 则关闭会话 调用onClose</p>
 */

@ServerEndpoint("/alarmListen/{userId}")
@Component
public class AlarmListener extends Listener  {

    private static final Logger LOGGER = LoggerFactory.getLogger(AlarmListener.class);
    @Resource
    private AlarmMapper alarmMapper;
    @Resource
    private LinkappUserMapper linkappUserMapper;
//    @Resource
//    private RedisUtil redisUtil;
    @Resource
    private RedissonClient redisson;
    public static String WS_ALARM_LISTEN = "WS_ALARM_LISTEN";
    private RTopic topic;

    private static CopyOnWriteArraySet<Listener> webSocketSet = new CopyOnWriteArraySet<>();

    @Override
    @OnOpen
    public void onOpen(Session session, @PathParam("tenantId") String tenantId, @PathParam("userId") String userId) {
        init(session, tenantId, userId);
        // 加入set中
        webSocketSet.add(this);
    }

    /**
     * 连接关闭调用的方法
     */
    @Override
    @OnClose
    public void onClose() {
        LOGGER.info("通知关闭连接,用户id:{}", getUserId());
        // 从set中删除
        webSocketSet.remove(this);
    }


    /**
     * 开启监听
     */
    @PostConstruct
    void openReceiving() {
        topic = redisson.getTopic(WS_ALARM_LISTEN, new SerializationCodec());
        LOGGER.info("监听ws成功：{}", topic);
        topic.addListener(Alarm.class, (charSequence, alarm) -> {
            send(alarm);
        });
    }

    public void sendNotice(Alarm alarm) {
//        redis 发广播
        try {
            topic.publish(alarm);
        } catch (Exception e) {
            LOGGER.error("sendNotice失败：", e);
        }

//        String key = WS_ALARM_LISTEN + UUID.randomUUID();
//        redisUtil.set(key+ "_param", alarm, 15);
////        10毫秒过期
//        redisUtil.setExpiredMilliseconds(key, "", 10);
    }

    /**
     * 发送消息
     */

    public void send(Alarm alarm1) {
        //        查询 刚生成的告警 alarm 可以被哪些用户可见
        Set<String> userIds = webSocketSet.stream().map(Listener::getUserId).collect(Collectors.toSet());
        if (ObjectUtils.isEmpty(userIds)) {
            return;
        }
        Alarm alarm = alarmMapper.getNoticeInfoAlarm(alarm1.getId());
        alarm.setDeviceName(alarm1.getDeviceName());
//过滤 租户相同 是管理员的用户id
        QueryWrapper qw = new QueryWrapper();
        qw.in("id", userIds);
        qw.eq("tenant_id", alarm.getTenantId());
        qw.eq("type", "1");
        qw.select("id");
        List<LinkappUser> adminUserIds = linkappUserMapper.selectList(qw);
        Set<String> userIdSet = adminUserIds.stream().map(e -> e.getId().toString()).collect(Collectors.toSet());
        userIdSet.addAll(alarmMapper.selectVisibleAlarmCommonUserIds(userIds, alarm));
        LOGGER.info("目前存在{}个会话", webSocketSet.size());
        for (Listener listener : webSocketSet) {
            if (userIdSet.contains(listener.getUserId())) {
                listener.sendMessage(JSON.toJSONString(alarm));
            }
        }
    }
}